﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace EnumGenerator
{
    public partial class FormMain : Form
    {
        #region [MESSAGE] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private class Message
        {
            public const string ProjectPathSaved = "Project Path kaydedildi.";
            public const string NamespaceSaved = "NameSpace kaydedildi.";
            public const string DefConnStrSaved = "ConnectionString Kaydedildi.";
            public const string DbConnOk = "Veritabanı bağlantısı yapıldı.";
            public const string NoDbConn = "Veritabanı bağlantısı olmadığı için bu işlem yapılamaz.";
            public const string EnumClassGenerated = "Enum Class başarıyla oluşturuldu";
            public const string NoDbConnString = "ConnectionString bulunamadı ! Bu nedenle veritabanı bağlantısı gerçekleştirilemedi.";
            public const string MandatoryProjectPath = "'Project Path' alanı boş bırakılamaz !";
            public const string MandatoryConStr = "Connection String giriniz !";
        }

        #region [VARIABLES] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private const string NewLine = "\r\n";
        private const string NewDblLine = "\r\n\r\n";
        private const string Tab = "\t";
        private const string TabX2 = "\t\t";

        private const string ProjectPathFileName = @"\ProjectPath.txt";
        private const string NamespacePathFileName = @"\LookupTables.txt";
        private const string ConnStrPathFileName = @"\DefConnStr.txt";

        private SqlConnection oCn;
        private string FullTableName { get { return string.Format("{0}.{1}.{2}", oCn.Database, comboBoxSchema.Text, comboBoxTable.Text); } }

        #endregion

        #endregion

        public FormMain()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            textBoxProjectPath.Text = GetProjectPath();
            textBoxNameSpace2.Text = GetNamespacePath();
            OpenConnectionAndLoadSchemas();
        }

        private string GetProjectPath()
        {
            string txt = string.Empty;
            string fullPath = string.Format("{0}{1}", Application.StartupPath, ProjectPathFileName);

            if (File.Exists(fullPath))
            {
                TextReader tr = new StreamReader(fullPath, Encoding.Default);
                txt = tr.ReadLine();
                tr.Close();
            }
            return txt;
        }

        private string GetNamespacePath()
        {
            string txt = string.Empty;
            string fullPath = string.Format("{0}{1}", Application.StartupPath, NamespacePathFileName);

            if (File.Exists(fullPath))
            {
                TextReader tr = new StreamReader(fullPath, Encoding.Default);
                txt = tr.ReadLine();
                tr.Close();
            }
            return txt;
        }

        private bool OpenConnectionAndLoadSchemas()
        {
            if (textBoxConnStr.Text == string.Empty)
            {
                DbConnFailed(Message.NoDbConnString);
                return false;
            }

            if (oCn != null) { if (oCn.State == ConnectionState.Open) oCn.Close(); }
            oCn = new SqlConnection(textBoxConnStr.Text);

            try
            {
                oCn.Open();
                DbConnSuccessful();
                return true;
            }
            catch (Exception ex)
            {
                DbConnFailed(ex.Message);
                return false;
            }
        }

        private void DbConnSuccessful()
        {
            LoadSchemas();
            labelConnectedDb.ForeColor = Color.Blue;
            labelConnectedDb.Text = string.Format("Connected To {0}", oCn.Database);
        }

        private void DbConnFailed(string pMessage)
        {
            MessageBox.Show(pMessage, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            labelConnectedDb.ForeColor = Color.DarkRed;
            labelConnectedDb.Text = "DataBase Connection Failed";
        }

        private void LoadSchemas()
        {
            string prefix = checkBoxOnlyTT.Checked ? "WHERE S.name LIKE 'TT_%'" : string.Empty;

            string sql = "SELECT S.name AS SchemaName,S.schema_id AS SchemaId FROM sys.schemas AS S {0} ORDER BY S.name";
            sql = string.Format(sql, prefix);

            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.Default);
            DataTable oDt = new DataTable();
            oDt.Load(dr);

            comboBoxSchema.DataSource = oDt;
            comboBoxSchema.DisplayMember = "SchemaName";
            comboBoxSchema.ValueMember = "SchemaId";

            comboBoxSchema.Text = "Please choose a schema";
        }

        private void LoadTables()
        {
            object selValue = comboBoxSchema.SelectedValue;
            if (selValue.ToString() == "System.Data.DataRowView") return;
            comboBoxTable.DataSource = null;

            string sql = @"SELECT  T.name AS TableName
                                  ,T.object_id AS TableId
                            FROM sys.schemas AS S INNER JOIN sys.tables AS T ON S.schema_id=T.schema_id
                            WHERE S.schema_id = @pSchemaId
                            ORDER BY S.name , T.name";

            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlParameter oPrm = new SqlParameter("@pSchemaId", SqlDbType.Int);
            oPrm.Value = selValue;
            cmd.Parameters.Add(oPrm);

            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.Default);
            DataTable oDt = new DataTable();
            oDt.Load(dr);

            comboBoxTable.DataSource = oDt;
            comboBoxTable.DisplayMember = "TableName";
            comboBoxTable.ValueMember = "TableId";

            LoadTableColumns();
        }

        private void LoadTableColumns()
        {
            object schemaID = comboBoxSchema.SelectedValue;
            object tableID = comboBoxTable.SelectedValue;
            comboBoxValueField.DataSource = null;
            comboBoxTextField.DataSource = null;
            comboBoxDescription.DataSource = null;

            if (schemaID == null || tableID == null) return;
            if (schemaID.ToString() == "System.Data.DataRowView" || tableID.ToString() == "System.Data.DataRowView") return;

            string sql = @" SELECT	C.[name] AS ColumnName
                                  , C.column_id AS ColumnId

                            FROM			SYS.OBJECTS AS O 
                                INNER JOIN  SYS.SCHEMAS AS S ON S.schema_id=O.schema_id
                                INNER JOIN  SYS.COLUMNS AS C ON C.[object_id]=O.[object_id]
                                INNER JOIN  SYS.TYPES	AS T ON T.system_type_id=C.system_type_id 

                            WHERE S.schema_id = @pSchemaId AND O.object_id = @pTabloId
                            ORDER BY C.column_id
                          ";

            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlParameter oPrm;
            oPrm = new SqlParameter("@pSchemaId", SqlDbType.Int);
            oPrm.Value = schemaID;
            cmd.Parameters.Add(oPrm);

            oPrm = new SqlParameter("@pTabloId", SqlDbType.Int);
            oPrm.Value = tableID;
            cmd.Parameters.Add(oPrm);

            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.Default);
            DataTable oDt1 = new DataTable();
            DataTable oDt2 = new DataTable();
            DataTable oDt3 = new DataTable();
            oDt1.Load(dr);
            oDt2 = oDt1.Copy();
            oDt3 = oDt1.Copy();

            if (oDt1.Rows.Count > 1)
            {
                comboBoxValueField.DataSource = oDt1;
                comboBoxValueField.DisplayMember = "ColumnName";
                comboBoxValueField.ValueMember = "ColumnId";
                comboBoxValueField.SelectedIndex = 0;

                comboBoxTextField.DataSource = oDt2;
                comboBoxTextField.DisplayMember = "ColumnName";
                comboBoxTextField.ValueMember = "ColumnId";
                comboBoxTextField.SelectedIndex = 1;

                comboBoxDescription.DataSource = oDt3;
                comboBoxDescription.DisplayMember = "ColumnName";
                comboBoxDescription.ValueMember = "ColumnId";
                int selIdx = 1; if (oDt3.Rows.Count > 2) selIdx = 2;
                comboBoxDescription.SelectedIndex = selIdx;
            }
            else
                MessageBox.Show("Seçilen tablo birden fazla kolon içermelidir !", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }

        private void SuggestNameSpace()
        {
            string secilenTablo = comboBoxSchema.Text;
            secilenTablo = secilenTablo.Replace("TT_", string.Empty);
            secilenTablo = ProperCase(secilenTablo);
            textBoxNameSpace2.Text = secilenTablo;
        }

        private void comboBoxSchema_SelectedIndexChanged(object sender, EventArgs e)
        {
            LoadTables();
            SuggestNameSpace();
        }

        private void comboBoxTable_SelectedIndexChanged(object sender, EventArgs e)
        {
            LoadTableColumns();
        }

        private void buttonGetTables_Click(object sender, EventArgs e)
        {
            if (oCn == null || oCn.State != ConnectionState.Open)
            {
                MessageBox.Show(Message.NoDbConn, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            LoadTables();
            SuggestNameSpace();
        }

        private void buttonTargetPath_Click(object sender, EventArgs e)
        {
            folderBrowserDialog1.ShowDialog();
            textBoxProjectPath.Text = folderBrowserDialog1.SelectedPath;
        }

        private void buttonGenerate_Click(object sender, EventArgs e)
        {
            if (!ValidateInputs()) return;
            string path = textBoxProjectPath.Text + @"\WcfServer\Types\Enumerations\" + textBoxNameSpace2.Text.Replace(".", @"\");
            string nameSpace = textBoxNameSpace1.Text + textBoxNameSpace2.Text;
            string className = ProperCase(comboBoxTable.Text); if (checkBoxConstant.Checked) className = className + "Const";
            string enumName = comboBoxTextField.Text;
            string enumValue = comboBoxValueField.Text;
            string enumDescription = comboBoxDescription.Text;
            string fullPath = path + @"\" + className + ".cs";

            string code = GenerateCode(nameSpace, className, enumName, enumValue, enumDescription);

            if (!string.IsNullOrEmpty(code))
            {
                try
                {
                    if (!Directory.Exists(path)) Directory.CreateDirectory(path);

                    TextWriter tw = new StreamWriter(fullPath, false, Encoding.UTF8);
                    tw.WriteLine(code);
                    tw.Close();

                    MessageBox.Show(Message.EnumClassGenerated, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception exc)
                {
                    MessageBox.Show(exc.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private bool ValidateInputs()
        {
            string msg = string.Empty;
            if (comboBoxTextField.Text == string.Empty) msg = "Text Field seçiniz !";
            if (comboBoxValueField.Text == string.Empty) msg = "Value Field seçiniz !";
            if (comboBoxTable.Text == string.Empty) msg = "Bir tablo seçiniz !";
            if (comboBoxSchema.Text == string.Empty) msg = "Bir schema seçiniz !";
            if (textBoxProjectPath.Text == string.Empty) msg = "Proje dizinini giriniz !";
            if (textBoxNameSpace2.Text == string.Empty) msg = "NameSpace giriniz !";
            if (textBoxConnStr.Text == string.Empty) msg = "Connection String giriniz !";

            if (msg != string.Empty)
            {
                MessageBox.Show(msg, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
            return true;
        }

        private string ProperCase(string s)
        {
            s = s.Replace("_", " ");
            s = s.ToLower(System.Globalization.CultureInfo.CurrentCulture);
            s = s.Replace("ı", "i");
            s = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(s);
            s = s.Replace("İ", "I");
            s = s.Replace(" ", "");
            return s;
        }

        private string GenerateCode(string pNameSpace, string pClassName, string pEnumName, string pEnumValue, string pEnumDescription)
        {
            string sql = string.Format(@"SELECT * FROM {0}", FullTableName);
            if (checkBoxUseSqlScript.Checked) sql = textBoxSqlScript.Text;

            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.Default);
            DataTable oDt = new DataTable();
            oDt.Load(dr);

            if (oDt.Rows.Count < 1)
            {
                MessageBox.Show("Seçilen tabloda kayıt bulunamadı !", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                return string.Empty;
            }

            string enumList = string.Empty;
            string propertyList = string.Empty;

            #region "Enum Creation"

            string clmnTypeName = GetColumnTypeName(pEnumValue);
            string type = GetType(clmnTypeName);

            if (!checkBoxConstant.Checked)
            {
                foreach (DataRow row in oDt.Rows)
                {
                    string name = CreateEnumName(row[pEnumName].ToString());
                    string value = row[pEnumValue].ToString();
                    string desc = row[pEnumDescription].ToString();

                    if (clmnTypeName.IndexOf("int") == -1)
                    {
                        MessageBox.Show("Value Field kolon tipi [tinyint, smallint, int, bigint] olabilir !", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        return "";
                    }
                    if (checkBoxWithDesc.Checked)
                        enumList += string.Format("[Description('{0}')] \r\n ||{1} = {2} , \r\n", desc, name, value) + (char)9 + (char)9;
                    else
                        enumList += string.Format("{0} = {1} , \r\n", name, value) + (char)9 + (char)9;

                    //propertyList += string.Format("public static {0} {1} [[ get [[ return {2}; ]] ]]", type, name, value) + "\r\n" + (char)9 + (char)9; ;
                    propertyList += string.Format("public const {0} {1} = {2};", type, name, value) + "\r\n" + (char)9 + (char)9; ;
                }
                enumList = enumList.Substring(0, enumList.Length - 5);  //5: sondaki virgül, \r, \n ve iki adet (char)9'dan kaynklı fazlalık. (,rntt)
                propertyList = "||" + propertyList;
            }
            else
            {
                foreach (DataRow row in oDt.Rows)
                {
                    string name = CreateEnumName(row[pEnumName].ToString());
                    string value = row[pEnumValue].ToString();
                    if (type == "string") value = (char)34 + value + (char)34;
                    enumList += "public const " + type + " " + name + " = " + value + ";" + "\r\n" + (char)9 + (char)9;
                }
            }
            #endregion

            #region"code string"
            string usings = @"
                using System.ComponentModel;
                ".Replace("  ", "");

            string code = string.Empty;
            if (!checkBoxConstant.Checked)
            {
                code = string.Format(@"{6}
                {5}

                namespace {0}
                [[
                    |public enum {1}
                    |[[
                        ||{4}
                    |]]
                    
                    |public struct {2}
                    |[[
                        {3}
                    |]]
                ]]
                ", pNameSpace, pEnumName, pClassName, propertyList, enumList, usings, PrepareHeaderComment());
            }
            else
            {
                code = string.Format(@"{3}

                namespace {0}
                [[
                    |public struct {1}
                    |[[
                        ||{2}
                    |]]
                ]]
                ", pNameSpace, pClassName, enumList, usings);
            }
            code = code.Replace("  ", "").Replace("|", "\t").Replace("[[", "{").Replace("]]", "}").Replace("'", ((char)34).ToString());
            #endregion

            return code;
        }

        private string GetType(string pColumnTypeName)
        {
            string type = string.Empty;
            if (pColumnTypeName == "tinyint") type = "byte";
            else if (pColumnTypeName == "smallint") type = "short";
            else if (pColumnTypeName == "int") type = "int";
            else if (pColumnTypeName == "bigint") type = "long";
            else if (pColumnTypeName == "bit") type = "bool";
            else if (pColumnTypeName.IndexOf("char") > -1) type = "string";
            else if (pColumnTypeName.IndexOf("real,money,float,decimal,numeric") > -1) type = "double";
            else type = "object";
            return type;
        }

        private string CreateEnumName(string s)
        {
            if (checkBoxUseEnumNameWithoutChange.Checked) return s;

            s = s.Replace("(", "").Replace(")", "").Replace("/", "").Replace("-", " ").Replace("  ", "");
            s = s.Replace("Ç", "C").Replace("Ğ", "G").Replace("İ", "I").Replace("Ö", "O").Replace("Ş", "S").Replace("Ü", "U");
            s = s.ToLower(System.Globalization.CultureInfo.CurrentCulture);
            s = s.Replace("ç", "c").Replace("ğ", "g").Replace("ı", "i").Replace("ö", "o").Replace("ş", "s").Replace("ü", "u");
            s = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(s);
            s = s.Replace("İ", "I");
            s = s.Trim();
            if (checkBoxSeparateEnumName.Checked)
                s = s.Replace(" ", "_");
            else
                s = s.Replace(" ", "");

            s = s.Replace(".", string.Empty);
            return s;
        }

        private string GetColumnTypeName(string pColumnName)
        {
            string sql = @" SELECT	T.name AS SysTypeName
                            FROM			SYS.OBJECTS AS O 
                                INNER JOIN  SYS.SCHEMAS AS S ON S.schema_id=O.schema_id
                                INNER JOIN  SYS.COLUMNS AS C ON C.[object_id]=O.[object_id]
                                INNER JOIN  SYS.TYPES	AS T ON T.system_type_id=C.system_type_id 

                            WHERE S.schema_id = @pSchemaId AND O.object_id = @pTableId AND C.name = @pColumnName";

            object schemaID = comboBoxSchema.SelectedValue;
            object tableID = comboBoxTable.SelectedValue;

            SqlCommand cmd = new SqlCommand(sql, oCn);
            SqlParameter oPrm;
            oPrm = new SqlParameter("@pSchemaId", SqlDbType.Int);
            oPrm.Value = schemaID;
            cmd.Parameters.Add(oPrm);

            oPrm = new SqlParameter("@pTableId", SqlDbType.Int);
            oPrm.Value = tableID;
            cmd.Parameters.Add(oPrm);

            oPrm = new SqlParameter("@pColumnName", SqlDbType.VarChar);
            oPrm.Value = pColumnName;
            cmd.Parameters.Add(oPrm);

            string typeName = (string)cmd.ExecuteScalar();
            return typeName;
        }

        private void buttonSaveProjectPath_Click(object sender, EventArgs e)
        {
            if (textBoxProjectPath.Text == string.Empty)
            {
                MessageBox.Show(Message.MandatoryProjectPath, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string fullPath = Application.StartupPath + @"\ProjectPath.txt";
            TextWriter tw = new StreamWriter(fullPath, false, Encoding.Default);
            tw.WriteLine(textBoxProjectPath.Text);
            tw.Close();
            MessageBox.Show(Message.ProjectPathSaved, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void buttonConnectToDb_Click(object sender, EventArgs e)
        {
            if (OpenConnectionAndLoadSchemas())
                MessageBox.Show(Message.DbConnOk, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void buttonSaveNameSpace_Click(object sender, EventArgs e)
        {
            string fullPath = Application.StartupPath + @"\NameSpace.txt";
            TextWriter tw = new StreamWriter(fullPath, false, Encoding.Default);
            tw.WriteLine(textBoxNameSpace2.Text);
            tw.Close();
            MessageBox.Show(Message.NamespaceSaved, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private string PrepareHeaderComment()
        {
            string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
            string comment = @"
                /* [GENERATOR COMMENT] {0}
                    
                 * This code was generated by 'ECG (Enum Class Generator)'
                 * Generation Time: {1} 
                 * Runtime Version: {2}
                 * 
                 * Settings:
                 *      ||Use Enum Name Without Any Change = {3}
                 *      ||Separate EnumName Word Parts = {4}
                 *      ||Create Enum With Description = {5}
                 *      ||Create Fields As Constant Variables = {6}
                 *      ||{7}
                 * Table: {8}
                 *
                 * WARNING: Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
                */
            ";

            int rhl = 106;    //maximum region header length
            string regionHeaderChar = new string('~', rhl - 17);
            string sqlScript = string.Empty;
            if (checkBoxUseSqlScript.Checked) sqlScript = string.Format("Use SQL Script = {0} -- Sql = {1} \r\n", checkBoxUseSqlScript.Checked.ToString(), textBoxSqlScript.Text.Replace("\r\n", " "));

            comment = string.Format(comment
                    , regionHeaderChar
                    , DateTime.Now
                    , version
                    , checkBoxUseEnumNameWithoutChange.Checked.ToString()
                    , checkBoxSeparateEnumName.Checked.ToString()
                    , checkBoxWithDesc.Checked.ToString()
                    , checkBoxConstant.Checked.ToString()
                    , sqlScript
                    , FullTableName
                    );

            comment = comment.Replace("  ", "").Replace("|", "\t").Replace("'", ((char)34).ToString());

            return comment;
        }

        private void checkBoxUseSqlScript_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxUseSqlScript.Checked)
            {
                groupBoxSqlScript.Visible = true;
                this.Height = 500;
            }
            else
            {
                groupBoxSqlScript.Visible = false;
                this.Height = 380;
            }
        }

        private void checkBoxOnlyTT_CheckedChanged(object sender, EventArgs e)
        {
            OpenConnectionAndLoadSchemas();
        }

        private void checkBoxUseEnumNameWithoutChange_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxUseEnumNameWithoutChange.Checked) checkBoxSeparateEnumName.Checked = false;
        }

        private void checkBoxSeparateEnumName_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxSeparateEnumName.Checked) checkBoxUseEnumNameWithoutChange.Checked = false;
        }

        private void buttonSaveConnStr_Click(object sender, EventArgs e)
        {
            if (textBoxConnStr.Text == string.Empty)
            {
                MessageBox.Show(Message.MandatoryConStr, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string fullPath = Application.StartupPath + ConnStrPathFileName;
            TextWriter tw = new StreamWriter(fullPath, false, Encoding.Default);
            tw.WriteLine(textBoxConnStr.Text);
            tw.Close();
            MessageBox.Show(Message.DefConnStrSaved, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}


